/*
 * PPD.c
 *
 *  Created on: 10/10/2011
 *      Author: utn_so
 */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdint.h>
#include "funcionesDeConsola.h"
#include "algoritmoPPD.h"
#include "Protocolo_NIPC.h"
#include "configLoader.h"

#define SOCK_PATH     "/home/utn_so/Desarrollo/Workspace/mysocket"

uint16_t posActualCabezal = 0;
sem_t sem_cola;
sem_t sem_trace;
sem_t sem_sacar;
sem_t fin_trace;
uint8_t conexion;
Sector sectorSacado = NULL;
uint8_t DISCO;
cola v_cola_inact;
tipoConfigPPD configuracion;
char *PATH;

typedef enum
{
	Lectura=1,
	Escritura=2,
	ConsolaPPD=3
}TipoMensaje;

void *t_insertar(void* v_cola);
void *t_sacar(void* v_cola);
void *t_conexion_consola(void* v_cola);

void *t_insertar(void* v_cola_act) {
	t_sector sector;
	if(!strcmp(configuracion.modo,"CONNECT")) conexion = conectarse_RAID(configuracion.ipRAID,
			configuracion.puertoRAID, configuracion.id_disco);
	if(!strcmp(configuracion.modo,"LISTEN")) conexion = conectarse_FS(configuracion.ipPPD,
			configuracion.puertoPPD, configuracion.id_disco);
	if (!conexion) exit(1);
	while (1) {
		sector = recibirSector(conexion);
		sem_wait(&sem_cola);
		if(!strcmp(configuracion.algoritmo,"SSTF")) PONER_EN_COLA_SSTF(sector, (cola) v_cola_act);
		/*if(!strcmp(configuracion.algoritmo,"F-SCAN")) ORDENAR_DESCENDENTE_FSCAN(sector, v_cola_act,v_cola_inact);*/
		sem_post(&sem_cola);
		sem_post(&sem_sacar);
	}
	return 0;
}

void *t_sacar(void* v_cola) {
	cola var_cola;
	var_cola = (cola)v_cola;
	sectorSacado = (Sector) malloc (sizeof(t_sector));
    //cola* punteroCola;
	//cola newCola;
	//newCola = (cola) malloc (sizeof(cola));
	while (1) {
		sem_wait(&sem_sacar);
		sem_wait(&sem_cola);
		if(sectorSacado->type == ConsolaPPD) sem_wait(&sem_trace);
		//punteroCola = &var_cola;
		QUITAR_DE_COLA(sectorSacado, (cola) var_cola);
		if((*var_cola) != NULL) sem_post(&sem_sacar);
		sem_post(&sem_cola);

		if(sectorSacado->type == Lectura){
				*sectorSacado =	leerSector(*sectorSacado,DISCO,configuracion.tiempo_lectura);
			    enviarSector(conexion, *sectorSacado); //Comentado hasta que este la conexion
				}

		if(sectorSacado->type == Escritura){
			escribirSector(*sectorSacado,DISCO,configuracion.tiempo_escritura);
			  enviarSector(conexion, *sectorSacado); //Comentado hasta que este la conexion
		}

		if(sectorSacado->type == ConsolaPPD) {sem_post(&sem_trace);}
	}
	free(sectorSacado);
	sectorSacado = NULL;
	return 0;
}

void *t_conexion_consola(void* v_cola) {
	cola var_cola;
	var_cola = (cola)v_cola;
	uint32_t tamano_socket;
	uint8_t Socket,consola,longitud_path,n=0;
	struct sockaddr_un local, remote;
	char mensaje[100];
	pid_t child_pid;

	if ((Socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
		perror("socket");
		exit(1);
	}

	local.sun_family = AF_UNIX;
	strcpy(local.sun_path, SOCK_PATH);
	unlink(local.sun_path);
	longitud_path = strlen(local.sun_path) + sizeof(local.sun_family);
	if (bind(Socket, (struct sockaddr *) &local, longitud_path) == -1) {
		perror("bind");
		exit(1);
	}

	if (listen(Socket, 1) == -1) {
		perror("listen");
		exit(1);
	}
	//printf("Esperando una conexion...\n");
	child_pid = fork();
	if (child_pid == 0) {
		/* Comienza a ejecutar el proceso hijo */

		execv("/home/utn_so/Desarrollo/Workspace/ConsolaPPD/Debug/ConsolaPPD",
				NULL);
		// argv son los argumentos del padre.
		/* Si continuó la ejecucion es porque fallo la llamada al exec */

		printf("No se ejecuto el execv\n");
		exit(0);
	}
	tamano_socket = sizeof(remote);
	if ((consola = accept(Socket, (struct sockaddr *) &remote, &tamano_socket)) == -1) {
		perror("accept");
		exit(1);
	}

	//printf("Connected.\n");

	while (n >= 0) {
		memset(&mensaje[0], '\0', sizeof(char) * strlen(mensaje));
		n = recv(consola, mensaje, 100, 0);
		if (mensaje[0] == 'e')
			break;
		if (n <= 0) {
			if (n < 0)
				perror("recv");
		}
		if (mensaje[0] == 'e')
			exit(0);

		interpretar(consola, mensaje, mensaje, var_cola, v_cola_inact);
	}
	close(consola);
	return(0);
}

int main(int argc, char **argv) {
	cola var_cola;
	var_cola = (cola)malloc(sizeof(cola));
	*var_cola = NULL;
	configuracion = leerArchConfigPPD();
	pthread_t tid_insertar;
	pthread_t tid_sacar;
	pthread_t tid_conexion_consola;
	pthread_attr_t attr_conexion_consola;
	pthread_attr_t attr_insertar;
	pthread_attr_t attr_sacar;

	PATH = (char*)malloc(strlen(argv[1])+1);
	strcpy(PATH, argv[1]); // pasamos el path del archivo cuando iniciamos el proceso
	DISCO = abrirArchivo();

	sem_init(&sem_cola, 0, 1);
	sem_init(&sem_sacar, 0, 0);
	sem_init(&sem_trace, 0, 0);
	sem_init(&fin_trace, 0, 0);

	/*Conexion con el Proceso Consola del PPD */
	/*
	pthread_attr_init(&attr_conexion_consola);
	pthread_create(&tid_conexion_consola, &attr_conexion_consola,
			(void*)t_conexion_consola, (void*)var_cola);*/

	/*Conectamos con el proceso FS o el RAID 1, e insertamos los pedidos según el algoritmo de planificacion*/

    pthread_attr_init(&attr_insertar);
    pthread_create(&tid_insertar, &attr_insertar, t_insertar, (void*) var_cola);

	/*Sacamos los pedidos de las colas para atenderlos y devolverlos al FS o al RAID 1 */
	pthread_attr_init(&attr_sacar);

	pthread_create(&tid_sacar, &attr_sacar, (void*)t_sacar, (void*) var_cola);

	pthread_join(tid_insertar, NULL);
	pthread_join(tid_sacar, NULL);
	pthread_join(tid_conexion_consola, NULL);

	return (0);
}
